{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "b8d9cd8a",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np # 导入 numpy 库\n",
    "import torch # 导入 torch 库\n",
    "import torch.nn as nn # 导入 torch.nn 库\n",
    "d_k = 64 # K(=Q) 维度\n",
    "d_v = 64 # V 维度\n",
    "# 定义缩放点积注意力类\n",
    "class ScaledDotProductAttention(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(ScaledDotProductAttention, self).__init__()        \n",
    "    def forward(self, Q, K, V, attn_mask):\n",
    "        #------------------------- 维度信息 --------------------------------        \n",
    "        # Q K V [batch_size, n_heads, len_q/k/v, dim_q=k/v] (dim_q=dim_k)\n",
    "        # attn_mask [batch_size, n_heads, len_q, len_k]\n",
    "        #----------------------------------------------------------------\n",
    "        # 计算注意力分数（原始权重）[batch_size，n_heads，len_q，len_k]\n",
    "        scores = torch.matmul(Q, K.transpose(-1, -2)) / np.sqrt(d_k) \n",
    "        #------------------------- 维度信息 --------------------------------        \n",
    "        # scores [batch_size, n_heads, len_q, len_k]\n",
    "        #-----------------------------------------------------------------        \n",
    "        # 使用注意力掩码，将 attn_mask 中值为 1 的位置的权重替换为极小值\n",
    "        #------------------------- 维度信息 -------------------------------- \n",
    "        # attn_mask [batch_size, n_heads, len_q, len_k], 形状和 scores 相同\n",
    "        #-----------------------------------------------------------------    \n",
    "        scores.masked_fill_(attn_mask, -1e9) \n",
    "        # 对注意力分数进行 softmax 归一化\n",
    "        weights = nn.Softmax(dim=-1)(scores) \n",
    "        #------------------------- 维度信息 -------------------------------- \n",
    "        # weights [batch_size, n_heads, len_q, len_k], 形状和 scores 相同\n",
    "        #-----------------------------------------------------------------         \n",
    "        # 计算上下文向量（也就是注意力的输出）, 是上下文信息的紧凑表示\n",
    "        context = torch.matmul(weights, V) \n",
    "        #------------------------- 维度信息 -------------------------------- \n",
    "        # context [batch_size, n_heads, len_q, dim_v]\n",
    "        #-----------------------------------------------------------------    \n",
    "        return context, weights # 返回上下文向量和注意力分数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "3d995ade",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义多头自注意力类\n",
    "d_embedding = 512  # Embedding 的维度\n",
    "n_heads = 8  # Multi-Head Attention 中头的个数\n",
    "batch_size = 3 # 每一批的数据大小\n",
    "class MultiHeadAttention(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(MultiHeadAttention, self).__init__()\n",
    "        self.W_Q = nn.Linear(d_embedding, d_k * n_heads) # Q的线性变换层\n",
    "        self.W_K = nn.Linear(d_embedding, d_k * n_heads) # K的线性变换层\n",
    "        self.W_V = nn.Linear(d_embedding, d_v * n_heads) # V的线性变换层\n",
    "        self.linear = nn.Linear(n_heads * d_v, d_embedding)\n",
    "        self.layer_norm = nn.LayerNorm(d_embedding)\n",
    "    def forward(self, Q, K, V, attn_mask): \n",
    "        #------------------------- 维度信息 -------------------------------- \n",
    "        # Q K V [batch_size, len_q/k/v, embedding_dim] \n",
    "        #-----------------------------------------------------------------        \n",
    "        residual, batch_size = Q, Q.size(0) # 保留残差连接\n",
    "        # 将输入进行线性变换和重塑，以便后续处理\n",
    "        q_s = self.W_Q(Q).view(batch_size, -1, n_heads, d_k).transpose(1,2)        \n",
    "        k_s = self.W_K(K).view(batch_size, -1, n_heads, d_k).transpose(1,2)\n",
    "        v_s = self.W_V(V).view(batch_size, -1, n_heads, d_v).transpose(1,2)\n",
    "        #------------------------- 维度信息 -------------------------------- \n",
    "        # q_s k_s v_s: [batch_size, n_heads, len_q/k/v, d_q=k/v]\n",
    "        #----------------------------------------------------------------- \n",
    "        # 将注意力掩码复制到多头 attn_mask: [batch_size, n_heads, len_q, len_k]\n",
    "        attn_mask = attn_mask.unsqueeze(1).repeat(1, n_heads, 1, 1)\n",
    "        #------------------------- 维度信息 -------------------------------- \n",
    "        # attn_mask [batch_size, n_heads, len_q, len_k]\n",
    "        #----------------------------------------------------------------- \n",
    "        # 使用缩放点积注意力计算上下文和注意力权重\n",
    "        context, weights = ScaledDotProductAttention()(q_s, k_s, v_s, attn_mask)\n",
    "        #------------------------- 维度信息 -------------------------------- \n",
    "        # context [batch_size, n_heads, len_q, dim_v]\n",
    "        # weights [batch_size, n_heads, len_q, len_k]\n",
    "        #----------------------------------------------------------------- \n",
    "        # 通过调整维度将多个头的上下文向量连接在一起\n",
    "        context = context.transpose(1, 2).contiguous().view(batch_size, -1, n_heads * d_v) \n",
    "        #------------------------- 维度信息 -------------------------------- \n",
    "        # context [batch_size, len_q, n_heads * dim_v]\n",
    "        #-----------------------------------------------------------------        \n",
    "        # 用一个线性层把连接后的多头自注意力结果转换，原始地嵌入维度\n",
    "        output = self.linear(context) \n",
    "        #------------------------- 维度信息 -------------------------------- \n",
    "        # output [batch_size, len_q, embedding_dim]\n",
    "        #-----------------------------------------------------------------        \n",
    "        # 与输入 (Q) 进行残差链接，并进行层归一化后输出\n",
    "        output = self.layer_norm(output + residual)\n",
    "        #------------------------- 维度信息 -------------------------------- \n",
    "        # output [batch_size, len_q, embedding_dim]\n",
    "        #-----------------------------------------------------------------        \n",
    "        return output, weights # 返回层归一化的输出和注意力权重"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "c3f83e36",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义逐位置前馈网络类\n",
    "class PoswiseFeedForwardNet(nn.Module):\n",
    "    def __init__(self, d_ff=2048):\n",
    "        super(PoswiseFeedForwardNet, self).__init__()\n",
    "        # 定义一维卷积层 1，用于将输入映射到更高维度\n",
    "        self.conv1 = nn.Conv1d(in_channels=d_embedding, out_channels=d_ff, kernel_size=1)\n",
    "        # 定义一维卷积层 2，用于将输入映射回原始维度\n",
    "        self.conv2 = nn.Conv1d(in_channels=d_ff, out_channels=d_embedding, kernel_size=1)\n",
    "        # 定义层归一化\n",
    "        self.layer_norm = nn.LayerNorm(d_embedding)\n",
    "    def forward(self, inputs): \n",
    "        #------------------------- 维度信息 -------------------------------- \n",
    "        # inputs [batch_size, len_q, embedding_dim]\n",
    "        #----------------------------------------------------------------                       \n",
    "        residual = inputs  # 保留残差连接 \n",
    "        # 在卷积层 1 后使用 ReLU 激活函数 \n",
    "        output = nn.ReLU()(self.conv1(inputs.transpose(1, 2))) \n",
    "        #------------------------- 维度信息 -------------------------------- \n",
    "        # output [batch_size, d_ff, len_q]\n",
    "        #----------------------------------------------------------------\n",
    "        # 使用卷积层 2 进行降维 \n",
    "        output = self.conv2(output).transpose(1, 2) \n",
    "        #------------------------- 维度信息 -------------------------------- \n",
    "        # output [batch_size, len_q, embedding_dim]\n",
    "        #----------------------------------------------------------------\n",
    "        # 与输入进行残差链接，并进行层归一化\n",
    "        output = self.layer_norm(output + residual) \n",
    "        #------------------------- 维度信息 -------------------------------- \n",
    "        # output [batch_size, len_q, embedding_dim]\n",
    "        #----------------------------------------------------------------\n",
    "        return output # 返回加入残差连接后层归一化的结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "51c52548",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 生成正弦位置编码表的函数，用于在 Transformer 中引入位置信息\n",
    "def get_sin_enc_table(n_position, embedding_dim):\n",
    "    #------------------------- 维度信息 --------------------------------\n",
    "    # n_position: 输入序列的最大长度\n",
    "    # embedding_dim: 词嵌入向量的维度\n",
    "    #-----------------------------------------------------------------    \n",
    "    # 根据位置和维度信息，初始化正弦位置编码表\n",
    "    sinusoid_table = np.zeros((n_position, embedding_dim))    \n",
    "    # 遍历所有位置和维度，计算角度值\n",
    "    for pos_i in range(n_position):\n",
    "        for hid_j in range(embedding_dim):\n",
    "            angle = pos_i / np.power(10000, 2 * (hid_j // 2) / embedding_dim)\n",
    "            sinusoid_table[pos_i, hid_j] = angle    \n",
    "    # 计算正弦和余弦值\n",
    "    sinusoid_table[:, 0::2] = np.sin(sinusoid_table[:, 0::2])  # dim 2i 偶数维\n",
    "    sinusoid_table[:, 1::2] = np.cos(sinusoid_table[:, 1::2])  # dim 2i+1 奇数维    \n",
    "    #------------------------- 维度信息 --------------------------------\n",
    "    # sinusoid_table 的维度是 [n_position, embedding_dim]\n",
    "    #----------------------------------------------------------------   \n",
    "    return torch.FloatTensor(sinusoid_table)  # 返回正弦位置编码表"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "d7d2e1be",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义填充注意力掩码函数\n",
    "def get_attn_pad_mask(seq_q, seq_k):\n",
    "    #------------------------- 维度信息 --------------------------------\n",
    "    # seq_q 的维度是 [batch_size, len_q]\n",
    "    # seq_k 的维度是 [batch_size, len_k]\n",
    "    #-----------------------------------------------------------------\n",
    "    batch_size, len_q = seq_q.size()\n",
    "    batch_size, len_k = seq_k.size()\n",
    "    # 生成布尔类型张量\n",
    "    pad_attn_mask = seq_k.data.eq(0).unsqueeze(1)  # <PAD>token 的编码值为 0\n",
    "    #------------------------- 维度信息 --------------------------------\n",
    "    # pad_attn_mask 的维度是 [batch_size，1，len_k]\n",
    "    #-----------------------------------------------------------------\n",
    "    # 变形为与注意力分数相同形状的张量 \n",
    "    pad_attn_mask = pad_attn_mask.expand(batch_size, len_q, len_k)\n",
    "    #------------------------- 维度信息 --------------------------------\n",
    "    # pad_attn_mask 的维度是 [batch_size，len_q，len_k]\n",
    "    #-----------------------------------------------------------------\n",
    "    return pad_attn_mask"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "620249f5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义编码器层类\n",
    "class EncoderLayer(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(EncoderLayer, self).__init__()        \n",
    "        self.enc_self_attn = MultiHeadAttention() # 多头自注意力层        \n",
    "        self.pos_ffn = PoswiseFeedForwardNet() # 位置前馈神经网络层\n",
    "    def forward(self, enc_inputs, enc_self_attn_mask):\n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # enc_inputs 的维度是 [batch_size, seq_len, embedding_dim]\n",
    "        # enc_self_attn_mask 的维度是 [batch_size, seq_len, seq_len]\n",
    "        #-----------------------------------------------------------------\n",
    "        # 将相同的 Q，K，V 输入多头自注意力层 , 返回的 attn_weights 增加了头数  \n",
    "        enc_outputs, attn_weights = self.enc_self_attn(enc_inputs, enc_inputs,\n",
    "                                               enc_inputs, enc_self_attn_mask)\n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # enc_outputs 的维度是 [batch_size, seq_len, embedding_dim] \n",
    "        # attn_weights 的维度是 [batch_size, n_heads, seq_len, seq_len]      \n",
    "        # 将多头自注意力 outputs 输入位置前馈神经网络层\n",
    "        enc_outputs = self.pos_ffn(enc_outputs) # 维度与 enc_inputs 相同\n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # enc_outputs 的维度是 [batch_size, seq_len, embedding_dim] \n",
    "        #-----------------------------------------------------------------\n",
    "        return enc_outputs, attn_weights # 返回编码器输出和每层编码器注意力权重"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "b4ea3c25",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义编码器类\n",
    "n_layers = 6  # 设置 Encoder 的层数\n",
    "class Encoder(nn.Module):\n",
    "    def __init__(self, corpus):\n",
    "        super(Encoder, self).__init__()        \n",
    "        self.src_emb = nn.Embedding(len(corpus.src_vocab), d_embedding) # 词嵌入层\n",
    "        self.pos_emb = nn.Embedding.from_pretrained( \\\n",
    "          get_sin_enc_table(corpus.src_len+1, d_embedding), freeze=True) # 位置嵌入层\n",
    "        self.layers = nn.ModuleList(EncoderLayer() for _ in range(n_layers))# 编码器层数\n",
    "    def forward(self, enc_inputs):  \n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # enc_inputs 的维度是 [batch_size, source_len]\n",
    "        #-----------------------------------------------------------------\n",
    "        # 创建一个从 1 到 source_len 的位置索引序列\n",
    "        pos_indices = torch.arange(1, enc_inputs.size(1) + 1).unsqueeze(0).to(enc_inputs)\n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # pos_indices 的维度是 [1, source_len]\n",
    "        #-----------------------------------------------------------------             \n",
    "        # 对输入进行词嵌入和位置嵌入相加 [batch_size, source_len，embedding_dim]\n",
    "        enc_outputs = self.src_emb(enc_inputs) + self.pos_emb(pos_indices)\n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # enc_outputs 的维度是 [batch_size, seq_len, embedding_dim]\n",
    "        #-----------------------------------------------------------------\n",
    "        # 生成自注意力掩码\n",
    "        enc_self_attn_mask = get_attn_pad_mask(enc_inputs, enc_inputs) \n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # enc_self_attn_mask 的维度是 [batch_size, len_q, len_k]        \n",
    "        #-----------------------------------------------------------------         \n",
    "        enc_self_attn_weights = [] # 初始化 enc_self_attn_weights\n",
    "        # 通过编码器层 [batch_size, seq_len, embedding_dim]\n",
    "        for layer in self.layers: \n",
    "            enc_outputs, enc_self_attn_weight = layer(enc_outputs, enc_self_attn_mask)\n",
    "            enc_self_attn_weights.append(enc_self_attn_weight)\n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # enc_outputs 的维度是 [batch_size, seq_len, embedding_dim] 维度与 enc_inputs 相同\n",
    "        # enc_self_attn_weights 是一个列表，每个元素的维度是 [batch_size, n_heads, seq_len, seq_len]          \n",
    "        #-----------------------------------------------------------------\n",
    "        return enc_outputs, enc_self_attn_weights # 返回编码器输出和编码器注意力权重"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "40db8a81",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 生成后续注意力掩码的函数，用于在多头自注意力计算中忽略未来信息\n",
    "def get_attn_subsequent_mask(seq):\n",
    "    #------------------------- 维度信息 --------------------------------\n",
    "    # seq 的维度是 [batch_size, seq_len(Q)=seq_len(K)]\n",
    "    #-----------------------------------------------------------------\n",
    "    # 获取输入序列的形状\n",
    "    attn_shape = [seq.size(0), seq.size(1), seq.size(1)]  \n",
    "    #------------------------- 维度信息 --------------------------------\n",
    "    # attn_shape 是一个一维张量 [batch_size, seq_len(Q), seq_len(K)]\n",
    "    #-----------------------------------------------------------------\n",
    "    # 使用 numpy 创建一个上三角矩阵（triu = triangle upper）\n",
    "    subsequent_mask = np.triu(np.ones(attn_shape), k=1)\n",
    "    #------------------------- 维度信息 --------------------------------\n",
    "    # subsequent_mask 的维度是 [batch_size, seq_len(Q), seq_len(K)]\n",
    "    #-----------------------------------------------------------------\n",
    "    # 将 numpy 数组转换为 PyTorch 张量，并将数据类型设置为 byte（布尔值）\n",
    "    subsequent_mask = torch.from_numpy(subsequent_mask).byte()\n",
    "    #------------------------- 维度信息 --------------------------------\n",
    "    # 返回的 subsequent_mask 的维度是 [batch_size, seq_len(Q), seq_len(K)]\n",
    "    #-----------------------------------------------------------------\n",
    "    return subsequent_mask # 返回后续位置的注意力掩码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "ff32a64b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义解码器层类\n",
    "class DecoderLayer(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(DecoderLayer, self).__init__()        \n",
    "        self.dec_self_attn = MultiHeadAttention() # 多头自注意力层       \n",
    "        self.dec_enc_attn = MultiHeadAttention()  # 多头自注意力层，连接编码器和解码器        \n",
    "        self.pos_ffn = PoswiseFeedForwardNet() # 位置前馈神经网络层\n",
    "    def forward(self, dec_inputs, enc_outputs, dec_self_attn_mask, dec_enc_attn_mask):\n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # dec_inputs 的维度是 [batch_size, target_len, embedding_dim]\n",
    "        # enc_outputs 的维度是 [batch_size, source_len, embedding_dim]\n",
    "        # dec_self_attn_mask 的维度是 [batch_size, target_len, target_len]\n",
    "        # dec_enc_attn_mask 的维度是 [batch_size, target_len, source_len]\n",
    "        #-----------------------------------------------------------------      \n",
    "        # 将相同的 Q，K，V 输入多头自注意力层\n",
    "        dec_outputs, dec_self_attn = self.dec_self_attn(dec_inputs, dec_inputs, \n",
    "                                                        dec_inputs, dec_self_attn_mask)\n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # dec_outputs 的维度是 [batch_size, target_len, embedding_dim]\n",
    "        # dec_self_attn 的维度是 [batch_size, n_heads, target_len, target_len]\n",
    "        #-----------------------------------------------------------------        \n",
    "        # 将解码器输出和编码器输出输入多头自注意力层\n",
    "        dec_outputs, dec_enc_attn = self.dec_enc_attn(dec_outputs, enc_outputs, \n",
    "                                                      enc_outputs, dec_enc_attn_mask)\n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # dec_outputs 的维度是 [batch_size, target_len, embedding_dim]\n",
    "        # dec_enc_attn 的维度是 [batch_size, n_heads, target_len, source_len]\n",
    "        #-----------------------------------------------------------------          \n",
    "        # 输入位置前馈神经网络层\n",
    "        dec_outputs = self.pos_ffn(dec_outputs)\n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # dec_outputs 的维度是 [batch_size, target_len, embedding_dim]\n",
    "        # dec_self_attn 的维度是 [batch_size, n_heads, target_len, target_len]\n",
    "        # dec_enc_attn 的维度是 [batch_size, n_heads, target_len, source_len]   \n",
    "        #-----------------------------------------------------------------\n",
    "        # 返回解码器层输出，每层的自注意力和解 - 编码器注意力权重\n",
    "        return dec_outputs, dec_self_attn, dec_enc_attn"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "cbd214a7",
   "metadata": {},
   "outputs": [],
   "source": [
    "#  定义解码器类\n",
    "n_layers = 6  # 设置 Decoder 的层数\n",
    "class Decoder(nn.Module):\n",
    "    def __init__(self, corpus):\n",
    "        super(Decoder, self).__init__()\n",
    "        self.tgt_emb = nn.Embedding(len(corpus.tgt_vocab), d_embedding) # 词嵌入层\n",
    "        self.pos_emb = nn.Embedding.from_pretrained( \\\n",
    "           get_sin_enc_table(corpus.tgt_len+1, d_embedding), freeze=True) # 位置嵌入层        \n",
    "        self.layers = nn.ModuleList([DecoderLayer() for _ in range(n_layers)]) # 叠加多层\n",
    "    def forward(self, dec_inputs, enc_inputs, enc_outputs): \n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # dec_inputs 的维度是 [batch_size, target_len]\n",
    "        # enc_inputs 的维度是 [batch_size, source_len]\n",
    "        # enc_outputs 的维度是 [batch_size, source_len, embedding_dim]\n",
    "        #-----------------------------------------------------------------   \n",
    "        # 创建一个从 1 到 source_len 的位置索引序列\n",
    "        pos_indices = torch.arange(1, dec_inputs.size(1) + 1).unsqueeze(0).to(dec_inputs)\n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # pos_indices 的维度是 [1, target_len]\n",
    "        #-----------------------------------------------------------------              \n",
    "        # 对输入进行词嵌入和位置嵌入相加\n",
    "        dec_outputs = self.tgt_emb(dec_inputs) + self.pos_emb(pos_indices)\n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # dec_outputs 的维度是 [batch_size, target_len, embedding_dim]\n",
    "         #-----------------------------------------------------------------        \n",
    "        # 生成解码器自注意力掩码和解码器 - 编码器注意力掩码\n",
    "        dec_self_attn_pad_mask = get_attn_pad_mask(dec_inputs, dec_inputs) # 填充位掩码\n",
    "        dec_self_attn_subsequent_mask = get_attn_subsequent_mask(dec_inputs) # 后续位掩码\n",
    "        dec_self_attn_mask = torch.gt((dec_self_attn_pad_mask \\\n",
    "                                       + dec_self_attn_subsequent_mask), 0) \n",
    "        dec_enc_attn_mask = get_attn_pad_mask(dec_inputs, enc_inputs) # 解码器 - 编码器掩码\n",
    "        #------------------------- 维度信息 --------------------------------        \n",
    "        # dec_self_attn_pad_mask 的维度是 [batch_size, target_len, target_len]\n",
    "        # dec_self_attn_subsequent_mask 的维度是 [batch_size, target_len, target_len]\n",
    "        # dec_self_attn_mask 的维度是 [batch_size, target_len, target_len]\n",
    "        # dec_enc_attn_mask 的维度是 [batch_size, target_len, source_len]\n",
    "         #-----------------------------------------------------------------       \n",
    "        dec_self_attns, dec_enc_attns = [], [] # 初始化 dec_self_attns, dec_enc_attns\n",
    "        # 通过解码器层 [batch_size, seq_len, embedding_dim]\n",
    "        for layer in self.layers:\n",
    "            dec_outputs, dec_self_attn, dec_enc_attn = layer(dec_outputs, enc_outputs, \n",
    "                                               dec_self_attn_mask, dec_enc_attn_mask)\n",
    "            dec_self_attns.append(dec_self_attn)\n",
    "            dec_enc_attns.append(dec_enc_attn)\n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # dec_outputs 的维度是 [batch_size, target_len, embedding_dim]\n",
    "        # dec_self_attns 是一个列表，每个元素的维度是 [batch_size, n_heads, target_len, target_len]\n",
    "        # dec_enc_attns 是一个列表，每个元素的维度是 [batch_size, n_heads, target_len, source_len]\n",
    "        #----------------------------------------------------------------- \n",
    "        # 返回解码器输出，解码器自注意力和解码器 - 编码器注意力权重       \n",
    "        return dec_outputs, dec_self_attns, dec_enc_attns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "331996e2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义 Transformer 模型\n",
    "class Transformer(nn.Module):\n",
    "    def __init__(self, corpus):\n",
    "        super(Transformer, self).__init__()        \n",
    "        self.encoder = Encoder(corpus) # 初始化编码器实例        \n",
    "        self.decoder = Decoder(corpus) # 初始化解码器实例\n",
    "        # 定义线性投影层，将解码器输出转换为目标词汇表大小的概率分布\n",
    "        self.projection = nn.Linear(d_embedding, len(corpus.tgt_vocab), bias=False)\n",
    "    def forward(self, enc_inputs, dec_inputs):\n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # enc_inputs 的维度是 [batch_size, source_seq_len]\n",
    "        # dec_inputs 的维度是 [batch_size, target_seq_len]\n",
    "        #-----------------------------------------------------------------        \n",
    "        # 将输入传递给编码器，并获取编码器输出和自注意力权重        \n",
    "        enc_outputs, enc_self_attns = self.encoder(enc_inputs)\n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # enc_outputs 的维度是 [batch_size, source_len, embedding_dim]\n",
    "        # enc_self_attns 是一个列表，每个元素的维度是 [batch_size, n_heads, src_seq_len, src_seq_len]        \n",
    "        #-----------------------------------------------------------------          \n",
    "        # 将编码器输出、解码器输入和编码器输入传递给解码器\n",
    "        # 获取解码器输出、解码器自注意力权重和编码器 - 解码器注意力权重     \n",
    "        dec_outputs, dec_self_attns, dec_enc_attns = self.decoder(dec_inputs, enc_inputs, enc_outputs)\n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # dec_outputs 的维度是 [batch_size, target_len, embedding_dim]\n",
    "        # dec_self_attns 是一个列表，每个元素的维度是 [batch_size, n_heads, tgt_seq_len, src_seq_len]\n",
    "        # dec_enc_attns 是一个列表，每个元素的维度是 [batch_size, n_heads, tgt_seq_len, src_seq_len]   \n",
    "        #-----------------------------------------------------------------                \n",
    "        # 将解码器输出传递给投影层，生成目标词汇表大小的概率分布\n",
    "        dec_logits = self.projection(dec_outputs)  \n",
    "        #------------------------- 维度信息 --------------------------------\n",
    "        # dec_logits 的维度是 [batch_size, tgt_seq_len, tgt_vocab_size]\n",
    "        #-----------------------------------------------------------------\n",
    "        # 返回逻辑值 ( 原始预测结果 ), 编码器自注意力权重，解码器自注意力权重，解 - 编码器注意力权重\n",
    "        return dec_logits, enc_self_attns, dec_self_attns, dec_enc_attns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "53f67d78",
   "metadata": {},
   "outputs": [],
   "source": [
    "sentences = [\n",
    "    ['咖哥 喜欢 小冰', 'KaGe likes XiaoBing'],\n",
    "    ['我 爱 学习 人工智能', 'I love studying AI'],\n",
    "    ['深度学习 改变 世界', ' DL changed the world'],\n",
    "    ['自然语言处理 很 强大', 'NLP is powerful'],\n",
    "    ['神经网络 非常 复杂', 'Neural-networks are complex'] ]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "21e7d566",
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import Counter # 导入 Counter 类\n",
    "# 定义 TranslationCorpus 类\n",
    "class TranslationCorpus:\n",
    "    def __init__(self, sentences):\n",
    "        self.sentences = sentences\n",
    "        # 计算源语言和目标语言的最大句子长度，并分别加 1 和 2 以容纳填充符和特殊符号\n",
    "        self.src_len = max(len(sentence[0].split()) for sentence in sentences) + 1\n",
    "        self.tgt_len = max(len(sentence[1].split()) for sentence in sentences) + 2\n",
    "        # 创建源语言和目标语言的词汇表\n",
    "        self.src_vocab, self.tgt_vocab = self.create_vocabularies()\n",
    "        # 创建索引到单词的映射\n",
    "        self.src_idx2word = {v: k for k, v in self.src_vocab.items()}\n",
    "        self.tgt_idx2word = {v: k for k, v in self.tgt_vocab.items()}\n",
    "    # 定义创建词汇表的函数\n",
    "    def create_vocabularies(self):\n",
    "        # 统计源语言和目标语言的单词频率\n",
    "        src_counter = Counter(word for sentence in self.sentences for word in sentence[0].split())\n",
    "        tgt_counter = Counter(word for sentence in self.sentences for word in sentence[1].split())        \n",
    "        # 创建源语言和目标语言的词汇表，并为每个单词分配一个唯一的索引\n",
    "        src_vocab = {'<pad>': 0, **{word: i+1 for i, word in enumerate(src_counter)}}\n",
    "        tgt_vocab = {'<pad>': 0, '<sos>': 1, '<eos>': 2, \n",
    "                     **{word: i+3 for i, word in enumerate(tgt_counter)}}        \n",
    "        return src_vocab, tgt_vocab\n",
    "    # 定义创建批次数据的函数\n",
    "    def make_batch(self, batch_size, test_batch=False):\n",
    "        input_batch, output_batch, target_batch = [], [], []\n",
    "        # 随机选择句子索引\n",
    "        sentence_indices = torch.randperm(len(self.sentences))[:batch_size]\n",
    "        for index in sentence_indices:\n",
    "            src_sentence, tgt_sentence = self.sentences[index]\n",
    "            # 将源语言和目标语言的句子转换为索引序列\n",
    "            src_seq = [self.src_vocab[word] for word in src_sentence.split()]\n",
    "            tgt_seq = [self.tgt_vocab['<sos>']] + [self.tgt_vocab[word] \\\n",
    "                         for word in tgt_sentence.split()] + [self.tgt_vocab['<eos>']]            \n",
    "            # 对源语言和目标语言的序列进行填充\n",
    "            src_seq += [self.src_vocab['<pad>']] * (self.src_len - len(src_seq))\n",
    "            tgt_seq += [self.tgt_vocab['<pad>']] * (self.tgt_len - len(tgt_seq))            \n",
    "            # 将处理好的序列添加到批次中\n",
    "            input_batch.append(src_seq)\n",
    "            output_batch.append([self.tgt_vocab['<sos>']] + ([self.tgt_vocab['<pad>']] * \\\n",
    "                                    (self.tgt_len - 2)) if test_batch else tgt_seq[:-1])\n",
    "            target_batch.append(tgt_seq[1:])        \n",
    "          # 将批次转换为 LongTensor 类型\n",
    "        input_batch = torch.LongTensor(input_batch)\n",
    "        output_batch = torch.LongTensor(output_batch)\n",
    "        target_batch = torch.LongTensor(target_batch)            \n",
    "        return input_batch, output_batch, target_batch\n",
    "# 创建语料库类实例\n",
    "corpus = TranslationCorpus(sentences)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "812c15f3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 0001 cost = 2.913645\n",
      "Epoch: 0002 cost = 3.205770\n",
      "Epoch: 0003 cost = 3.022395\n",
      "Epoch: 0004 cost = 2.334429\n",
      "Epoch: 0005 cost = 1.382921\n"
     ]
    }
   ],
   "source": [
    "import torch # 导入 torch\n",
    "import torch.optim as optim # 导入优化器\n",
    "model = Transformer(corpus) # 创建模型实例\n",
    "criterion = nn.CrossEntropyLoss() # 损失函数\n",
    "optimizer = optim.Adam(model.parameters(), lr=0.0001) # 优化器\n",
    "epochs = 5 # 训练轮次\n",
    "for epoch in range(epochs): # 训练 100 轮\n",
    "    optimizer.zero_grad() # 梯度清零\n",
    "    enc_inputs, dec_inputs, target_batch = corpus.make_batch(batch_size) # 创建训练数据        \n",
    "    outputs, _, _, _ = model(enc_inputs, dec_inputs) # 获取模型输出 \n",
    "    loss = criterion(outputs.view(-1, len(corpus.tgt_vocab)), target_batch.view(-1)) # 计算损失\n",
    "    if (epoch + 1) % 1 == 0: # 打印损失\n",
    "        print(f\"Epoch: {epoch + 1:04d} cost = {loss:.6f}\")\n",
    "    loss.backward()# 反向传播        \n",
    "    optimizer.step()# 更新参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "d031ceeb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "编码器输入 : tensor([[11, 12, 13,  0,  0]])\n",
      "解码器输入 : tensor([[1, 0, 0, 0, 0]])\n",
      "目标数据 : tensor([[14, 15, 16,  2,  0]])\n",
      "自然语言处理 很 强大 <pad> <pad> -> ['NLP', 'NLP', 'NLP', 'NLP', 'NLP']\n"
     ]
    }
   ],
   "source": [
    "# 创建一个大小为 1 的批次，目标语言序列 dec_inputs 在测试阶段，仅包含句子开始符号 <sos>\n",
    "enc_inputs, dec_inputs, target_batch = corpus.make_batch(batch_size=1,test_batch=True) \n",
    "print(\"编码器输入 :\", enc_inputs) # 打印编码器输入\n",
    "print(\"解码器输入 :\", dec_inputs) # 打印解码器输入\n",
    "print(\"目标数据 :\", target_batch) # 打印目标数据\n",
    "predict, enc_self_attns, dec_self_attns, dec_enc_attns = model(enc_inputs, dec_inputs) # 用模型进行翻译\n",
    "predict = predict.view(-1, len(corpus.tgt_vocab)) # 将预测结果维度重塑\n",
    "predict = predict.data.max(1, keepdim=True)[1] # 找到每个位置概率最大的词汇的索引\n",
    "# 解码预测的输出，将所预测的目标句子中的索引转换为单词\n",
    "translated_sentence = [corpus.tgt_idx2word[idx.item()] for idx in predict.squeeze()]\n",
    "# 将输入的源语言句子中的索引转换为单词\n",
    "input_sentence = ' '.join([corpus.src_idx2word[idx.item()] for idx in enc_inputs[0]])\n",
    "print(input_sentence, '->', translated_sentence) # 打印原始句子和翻译后的句子"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
